#pragma once

#include <stdio.h>

struct GridMapFileHeader
{
    unsigned int mapMagic;
    unsigned int versionMagic;
    unsigned int buildMagic;
    unsigned int areaMapOffset;
    unsigned int areaMapSize;
    unsigned int heightMapOffset;
    unsigned int heightMapSize;
    unsigned int liquidMapOffset;
    unsigned int liquidMapSize;
    unsigned int holesOffset;
    unsigned int holesSize;
};

#define MAP_AREA_NO_AREA      0x0001

struct GridMapAreaHeader
{
    unsigned int fourcc;
    unsigned short flags;
    unsigned short gridArea;
};

#define MAP_HEIGHT_NO_HEIGHT  0x0001
#define MAP_HEIGHT_AS_INT16   0x0002
#define MAP_HEIGHT_AS_INT8    0x0004

struct GridMapHeightHeader
{
    unsigned int fourcc;
    unsigned int flags;
    float gridHeight;
    float gridMaxHeight;
};

#define MAP_LIQUID_NO_TYPE    0x0001
#define MAP_LIQUID_NO_HEIGHT  0x0002

struct GridMapLiquidHeader
{
    unsigned int fourcc;
    unsigned short flags;
    unsigned short liquidType;
    unsigned char offsetX;
    unsigned char offsetY;
    unsigned char width;
    unsigned char height;
    float liquidLevel;
};

enum GridMapLiquidStatus
{
    LIQUID_MAP_NO_WATER = 0x00000000,
    LIQUID_MAP_ABOVE_WATER = 0x00000001,
    LIQUID_MAP_WATER_WALK = 0x00000002,
    LIQUID_MAP_IN_WATER = 0x00000004,
    LIQUID_MAP_UNDER_WATER = 0x00000008
};

struct GridMapLiquidData
{
    unsigned int type_flags;
    unsigned int entry;
    float level;
    float depth_level;
};

#define MAX_HEIGHT            100000.0f                     // can be use for find ground height at surface
#define INVALID_HEIGHT       -100000.0f                     // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE
#define INVALID_HEIGHT_VALUE -200000.0f                     // for return, must be equal to VMAP_INVALID_HEIGHT_VALUE, check value for unknown height is VMAP_INVALID_HEIGHT
#define MAX_FALL_DISTANCE     250000.0f                     // "unlimited fall" to find VMap ground if it is available, just larger than MAX_HEIGHT - INVALID_HEIGHT
#define DEFAULT_HEIGHT_SEARCH     10.0f                     // default search distance to find height at nearby locations
#define DEFAULT_WATER_SEARCH      50.0f                     // default search distance to case detection water level

#define MAP_LIQUID_TYPE_NO_WATER    0x00
#define MAP_LIQUID_TYPE_MAGMA       0x01
#define MAP_LIQUID_TYPE_OCEAN       0x02
#define MAP_LIQUID_TYPE_SLIME       0x04
#define MAP_LIQUID_TYPE_WATER       0x08

#define MAP_ALL_LIQUIDS   (MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_MAGMA | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_SLIME)

#define MAP_LIQUID_TYPE_DARK_WATER  0x10
#define MAP_LIQUID_TYPE_WMO_WATER   0x20

class GridMap
{
public:

    GridMap();
    ~GridMap();

    bool loadData(char* filaname);
    void unloadData();

    static bool ExistMap(unsigned int mapid, int gx, int gy);
    static bool ExistVMap(unsigned int mapid, int gx, int gy);

    unsigned short getArea(float x, float y);
    float getHeight(float x, float y) { return (this->*m_gridGetHeight)(x, y); }
    float getLiquidLevel(float x, float y);
    unsigned char getTerrainType(float x, float y);
    GridMapLiquidStatus getLiquidStatus(float x, float y, float z, unsigned char ReqLiquidType, GridMapLiquidData* data = 0);

private:

    bool loadAreaData(FILE* in, unsigned int offset, unsigned int size);
    bool loadHeightData(FILE* in, unsigned int offset, unsigned int size);
    bool loadGridMapLiquidData(FILE* in, unsigned int offset, unsigned int size);
    bool loadHolesData(FILE* in, unsigned int offset, unsigned int size);
    bool isHole(int row, int col) const;

    // Get height functions and pointers
    typedef float(GridMap::* pGetHeightPtr)(float x, float y) const;
    pGetHeightPtr m_gridGetHeight;
    float getHeightFromFloat(float x, float y) const;
    float getHeightFromUInt16(float x, float y) const;
    float getHeightFromUInt8(float x, float y) const;
    float getHeightFromFlat(float x, float y) const;

private:
    unsigned short m_holes[16][16];
    unsigned int m_flags;

    // Area data
    unsigned short m_gridArea;
    unsigned short* m_area_map;

    // Height level data
    float m_gridHeight;
    float m_gridIntHeightMultiplier;
    union
    {
        float* m_V9;
        unsigned short* m_uint16_V9;
        unsigned char* m_uint8_V9;
    };
    union
    {
        float* m_V8;
        unsigned short* m_uint16_V8;
        unsigned char* m_uint8_V8;
    };

    // Liquid data
    unsigned short m_liquidType;
    unsigned char m_liquid_offX;
    unsigned char m_liquid_offY;
    unsigned char m_liquid_width;
    unsigned char m_liquid_height;
    float m_liquidLevel;
    unsigned short* m_liquidEntry;
    unsigned char* m_liquidFlags;
    float* m_liquid_map;
};
